
let mDpiFactor = null

let inchOfDistance = (d) => 
{
    if (!mDpiFactor) 
    {
        mDpiFactor = 0.5 * (cc.view._scaleX + cc.view._scaleY)
        mDpiFactor = mDpiFactor / 72
    }
    return d * mDpiFactor
}

let mFloor = Math.floor, mRound = Math.round
let mRandom = Math.random

Math.randomInt = (min, max) => { return mFloor(mRandom() * (max - min) + min) }

let randomBit = (num) =>
{

    let b = 0

    while (num > 0) 
    {
        num = num >> 1
        ++b
    }
    return b
}

let randomBin = (times) =>
{

    let num = mRound(mRandom())

    while (times > 1)
    {
        num = (num << 1) + mRound(mRandom())
        --times
    }

    return num
}

Math.randomInt2 = (min, max) =>
{
    let num = max - min

    let times = randomBit(num), binNum = 0

    do
    {
        binNum = randomBin(times)

    } while (!(binNum <= num))

    return binNum + min
}

let mBit100 = randomBit(100)

Math.random100 = () =>
{

    let binNum = 0

    do
    {
        binNum = randomBin(mBit100)

    } while (!(binNum <= 100))

    return binNum
}

let encodeUtf8 = (text) =>
{
    const code = encodeURIComponent(text)
    const bytes = []

    for (let i = 0; i < code.length; ++i)
    {
        const c = code.charAt(i)

        if (c === '%')
        {
            const hex = code.charAt(i + 1) + code.charAt(i + 2)
            const hexVal = parseInt(hex, 16)
            bytes.push(hexVal)

            i += 2
        } 
        else bytes.push(c.charCodeAt(0))
    }
    return bytes
}

let destroyNode = (node) =>{

    let v = null

    while(!0)
    {
        v = node.children[0]
        if (!v)
        {
            break
        } 
        destroyNode(v)
    }

    node.removeFromParent(true)
    node.destroy()
}

let seekNode = (path, parent) =>
{
    if (!parent)
    {
        parent = cc.director.getScene()
        if (!parent)
        {
            Log.e("seek node fail, active scene null : " + path)
            return null
        }
    }

    let names = path.split('.')
    if (names)
    {
        let num = names.length
        if (num > 0)
        {
            let node = parent.getChildByName(names[0])

            if (node && num > 1)
            {
                let i = 1

                while (i < num)
                {
                    node = node.getChildByName(names[i])
                    if (!node)
                    {
                        Log.w("seek node fail: " + path)

                        return null
                    }
                    ++i
                }
            }
            return node
        }
    }

    Log.e("seek node fail: " + path)

    return null
}

const mEventType = cc.Node.EventType

let bindNodeTouchEvent = (node, upHandler, sound) =>
{
    if (!node)
    {
        Log.w('bind node touch event fail, node is null')

        return
    }

    node.targetOff(node)

    if (upHandler)
    {
        node.on(
            mEventType.TOUCH_END,
            (e) =>
            {
                upHandler(e)

                if (sound)
                {
                    let as = require("AudioSystem").share()
                    as.play(sound)
                }
            },
            node
        )
    }
}


let bindNodeTouchEventAll = (node, upHandler, downHandler, moveHandler, cancelHandler, sound, disSound) =>
{

    if (!node)
    {
        Log.w('bind node touch event fail, node is null')

        return
    }

    node.targetOff(node)

    if (downHandler)
    {
        node.on(mEventType.TOUCH_START, downHandler, node)
    }

    if (upHandler)
    {
        node.on(
            mEventType.TOUCH_END,
            (e) =>
            {
                upHandler(e)

                if (!disSound && sound && sound.length > 0)
                {
                    let as = require("AudioSystem").share()
                    as.play(sound)
                }
            },
            node
        )
    }

    if (moveHandler)
    {
        node.on(mEventType.TOUCH_MOVE, moveHandler, node)
    }

    if (cancelHandler)
    {
        node.on(mEventType.TOUCH_CANCEL, cancelHandler, node)
    }
}

let bindTouchEvent = (parent, name, upHandler, downHandler, moveHandler, cancelHandler, sound, disSound) =>
{
    let node = seekNode(name, parent)
    if (!node)
    {
        Log.w('bind touch event fail, node is null : ', name)

        return null
    }

    bindNodeTouchEvent(node, upHandler, downHandler, moveHandler, cancelHandler, sound, disSound)

    return node
}

let bindTouchEventAll = (parent, name, upHandler, downHandler, moveHandler, cancelHandler, sound, disSound) =>
{
    let node = seekNode(name, parent)
    if (!node)
    {
        Log.w('bind touch event fail, node is null : ', name)

        return null
    }

    bindNodeTouchEventAll(node, upHandler, downHandler, moveHandler, cancelHandler, sound, disSound)

    return node
}

let bindNodeLongTouchEvent = (node, upHandler, downHandler, moveHandler, cancelHandler, sound, delayFun, delayTime) =>
{
    if (!node)
    {
        return
    }

    node.targetOff(node)

    let canBack = false
    let canGo   = false

    node.on(mEventType.TOUCH_START,
        (e) =>
        {
            canBack = true

            if (delayTime)
            {
                node.stopAllActions()

                node.runAction(
                    cc.sequence(cc.delayTime(delayTime),
                        cc.callFunc(() =>
                        {
                            if (canBack)
                            {
                                delayFun(e)
                                canGo = true
                            }
                        }
                        )
                    )
                )
            }
            else
            {
                if (downHandler)
                {
                    downHandler(e)
                }
            }
        }, node
    )

    node.on(
        mEventType.TOUCH_END,
        (e) =>
        {

            canBack = false

            if (canGo)
            {
                canGo = false
                if (cancelHandler)
                {
                    cancelHandler(e)
                }
            }
            else
            {
                if (upHandler)
                {
                    upHandler(e)
                }
            }
        },
        node
    )

    if (moveHandler)
    {
        node.on(mEventType.TOUCH_MOVE, moveHandler, node)
    }

    if (cancelHandler)
    {
        node.on(mEventType.TOUCH_CANCEL, cancelHandler, node)
    }
}

let bindLongTouchEvent = (parent, name, upHandler, downHandler, moveHandler, cancelHandler, sound, delayFun, delayTime) =>
{
    let node = seekNode(name, parent)
    if (node)
    {
        bindNodeLongTouchEvent(node, upHandler, downHandler, moveHandler, cancelHandler, sound, delayFun, delayTime)
    }

    return node
}

let loadResource = (name, type, times, callback) =>
{
    let tryTimes = times || 0

    let cb = (err, res) =>
        {
            if (err)
            {
                --tryTimes

                if (tryTimes > 0)
                {
                    loadResource(name, type, tryTimes, callback)

                    return
                }
                else
                {
                    Log.w('load resource, name: ' + name + 'fail: ' + err.message, ', stack: ', err.stack)
                }
                res = null
            }

            callback && callback(res)
        }

    if (type)
    {
        cc.resources.load(name, type, cb)
    }
    else
    {
        cc.resources.load(name, cb)
    }
}

let loadSpriteFrame = (name, callback) =>
{
    loadResource(name, cc.SpriteFrame, 3, callback)
}

let loadSpriteFrameAtlas = (atlasName, name, callback) =>
{
    loadResource(atlasName, cc.SpriteAtlas, 3, 
        (atlas)=>{

            if (atlas)
            {
                callback && callback(atlas.getSpriteFrame(name))
            }
            else
            {
                Log.e("ogre, load sprite frame from atlas < " + atlasName + " > fail: " + name)
            }
        }
    )
}

let loadPrefab = (name, times, callback) =>
{
    loadResource(name, cc.Prefab, times, callback)
}


let utf8LengthByString = (str) =>
{
    let len = 0

    for (var i = 0; i < str.length; ++i)
    {
        if (str.charCodeAt(i) > 127 || str.charCodeAt(i) == 94)
        {
            len += 2
        }
        else
        {
            ++len
        }
    }
    return len
}

let filterAsName = (s) =>
{
    if (!s)
    {
        return ''
    }

    s.replace(/\uD83C[\uDF00-\uDFFF]|\uD83D[\uDC00-\uDE4F]/g, "")
    s = s.replace(/\s*/g, "")

    let pattern = new RegExp("[`~!@#$^&*()=|{}':;',\\[\\].<>/?~！@#￥……&*（）——|{}【】‘；：”“'。，、？]")

    let rs = ""
    for (let i = 0; i < s.length; ++i) 
    {
        rs = rs + s.substr(i, 1).replace(pattern, '')
    }
    return s
}

let copyToClipboard = (text) =>
{
    if (cc.sys.platform === cc.sys.WECHAT_GAME)
    {
        wx.setClipboardData({ data: '' + text })
    }
    else if (cc.sys.isNative)
    {
        if (cc.sys.os == cc.sys.OS_ANDROID)
        {
            jsb.reflection.callStaticMethod("com/mi/AndroidBridge", "copyToClipboard", "(Ljava/lang/String;)V", text)
        }
        else if (cc.sys.os == cc.sys.OS_IOS)
        {
            jsb.reflection.callStaticMethod('AppleBridge', 'copyToClipboard:', text)
        }
    }
    else
    {
        if (cc.sys.os == cc.sys.OS_IOS)
        {
            let textArea   = document.createElement('textArea')
            textArea.value = text
            document.body.appendChild(textArea)

            let range = document.createRange()
            range.selectNodeContents(textArea)

            letselection = window.getSelection()
            selection.removeAllRanges()
            selection.addRange(range)
            textArea.setSelectionRange(0, 999999)

            document.execCommand('copy')
            document.body.removeChild(textArea)
        }
        else
        {
            if (window.clipboardData && window.clipboardData.setData)
            {
                window.clipboardData.setData('text', text)
            }
            else
            {
                let __copyToClipboard = (e) =>
                    {
                        e.clipboardData.setData('text/plain', text)
                        e.preventDefault()
                    }
                document.addEventListener('copy', __copyToClipboard)
                document.execCommand('copy')
                document.removeEventListener('copy', __copyToClipboard)
            }
        }
        
    }
}

let loadSpriteFrameAnimation = (name, parent, times, startCallback, endCallback) =>
{
    loadResource(name, cc.SpriteAtlas, 3,
        (atlas) =>
        {

            if (atlas && parent.isValid)
            {
                let frames = atlas.getSpriteFrames()
                if (frames)
                {
                    let num = frames.length
                    if (num > 0)
                    {
                        let clip = cc.AnimationClip.createWithSpriteFrames(frames, num)
                        if (clip)
                        {
                            clip.name = '1'

                            clip.wrapMode = times == 1 ? cc.WrapMode.Normal : cc.WrapMode.Loop
                            clip.repeatCount = times > 0 ? times : Infinity

                            let node = new cc.Node()

                            let sprite = node.addComponent(cc.Sprite)
                            sprite.spriteFrame = frames[0]

                            let animation = node.addComponent(cc.Animation)

                            animation.addClip(clip)

                            parent.addChild(node)

                            if (startCallback)
                            {
                                startCallback(node, clip)
                            }

                            let s = animation.play('1')

                            if (endCallback)
                            {
                                s.on("finished",
                                    (eventName, animationState) =>
                                    {
                                        if (animationState == s) 
                                        {
                                            s = null

                                            if (endCallback)
                                            {
                                                endCallback(node)
                                            }
                                        }
                                    }
                                )
                            }
                            return
                        }
                    }
                }

                if (startCallback)
                {
                    startCallback()
                }
            }
            else
            {
                if (startCallback)
                {
                    startCallback()
                }
            }
        }
    )
}

let releaseResource = (name, resType) =>
{
    let asset = cc.resources.get(name, resType)
    if (asset)
    {
        cc.assetManager.releaseAsset(asset)
    }
}

let releasePrefab = (name) =>
{
    releaseResource(name, cc.Prefab)
}

let releaseTexture = (texture) =>
{

    cc.assetManager.releaseAsset(texture)
}

let releaseNodeResource = (node) =>
{
    if (node)
    {
        let com = node.getComponent(cc.Sprite)
        if (com)
        {
            com = com.spriteFrame
            if (com)
            {
                releaseResource(com.getTexture(), null)
            }
        }
        else
        {
            com = node.getComponent(sp.Skeleton)
            if (com)
            {
                com = com.skeletonData
                if (com)
                {
                    releaseResource(com, null)
                }
            }
        }
    }
}

let releaseSpriteAtlas = (name) =>
{

    let atlas = cc.resources.get(name, cc.SpriteAtlas)
    if (atlas)
    {
        let frames = atlas._spriteFrames
        if (frames)
        {
            for (let k in frames)
            {
                cc.assetManager.releaseAsset(frames[k])
            }
        }

        cc.assetManager.releaseAsset(atlas)
    }
}


let Uint8ArrayToString = (fileData) =>
{
    let dataString = ""

    for (let i = 0; i < fileData.length; ++i) 
    {
        dataString += String.fromCharCode(fileData[i])
    }
   
    return dataString
}


let attachCaptcha = (node, data, callback)=>{

    if (cc.sys.os == cc.sys.OS_WINDOWS || !cc.sys.isNative) 
    {
        let img = new Image()
       
        img.src    = 'data:image/png;base64,' + data

        img.onload =  ()=> {
            
            if (!node.isValid)
            {
                img = null
                return
            }

            let sp = node.getComponent(cc.Sprite)
            if (sp)
            {
                let texture = new cc.Texture2D()

                texture.initWithElement(img)
                texture.handleLoadedTexture()

                sp.spriteFrame = new cc.SpriteFrame(texture)

                callback && callback(true)
            }

            img = null
        }
    }
    else 
    {
        let d = this.base64ToUint8Array('data:image/png;base64,' + data)
        if (d)
        {
            let fs = jsb.fileUtils
            let path = fs.getWritablePath() + 'img/'

            if (!fs.isDirectoryExist(path)) 
            {
                fs.createDirectory(path)
            }

            let filepath = path + 'captcha.png'

            if (fs.isFileExist(filepath)) 
            {
                fs.removeFile(filepath)
                cc.assetManager.releaseAsset(filepath)
            }

            if (fs.writeDataToFile(new Uint8Array(d), filepath)) 
            {
                cc.resources.load(filepath, (err, tex)=> {

                    if (tex && node.isValid)
                    {
                        let sp1 = node.getComponent(cc.Sprite)
                        if (sp1)
                        {
                            sp1.spriteFrame = new cc.SpriteFrame(tex)

                            callback && callback(true)
                        }
                    }
                })
            }
        }
    }
}

let convertArrayBufferToBase64 = (buffer) =>
{
    let binary = ''

    let bytes = new Uint8Array(buffer)
    
    for (let i = 0, len = bytes.byteLength; i < len; ++i) 
    {
        binary += String.fromCharCode(bytes[i])
    }
    return window.btoa(binary)
}

let requestDeviceOrientation = (landscapse, callback)=>{

    if (cc.sys.isNative)
    {
        if (cc.sys.os == cc.sys.OS_ANDROID)
        {
            window.RequestedOrientationCallback = ()=>{

                callback && callback()
                
                window.RequestedOrientationCallback = null
            }
            
            Log.i('ogre', 'request device orientation:: ' + (landscapse ?  'landscapse' : 'portrait'))

            jsb.reflection.callStaticMethod("com/mi/AndroidBridge", "setRequestedOrientation", "(Z)V", landscapse)
        }
    }
}

window.ogre = {

    storage : require('StorageSystem').share(),
    localize : require('LocalizeService'),

    inchOf : inchOfDistance,

    destroyNode : destroyNode,
    seekNode: seekNode,

    encodeUtf8: encodeUtf8,

    bindNodeTouchEvent: bindNodeTouchEvent,
    bindTouchEvent: bindTouchEvent,

    bindNodeTouchEventAll: bindNodeTouchEventAll,
    bindTouchEventAll: bindTouchEventAll,

    bindNodeLongTouchEvent: bindNodeLongTouchEvent,
    bindLongTouchEvent: bindLongTouchEvent,

    loadSpriteFrame: loadSpriteFrame,

    loadSpriteFrameAnimation: loadSpriteFrameAnimation,

    loadSpriteFrameAtlas: loadSpriteFrameAtlas,

    releasePrefab: releasePrefab,
    releaseTexture: releaseTexture,
    releaseResource: releaseResource,
    releaseNodeResource: releaseNodeResource,
    releaseSpriteAtlas: releaseSpriteAtlas,

    utf8LengthByString: utf8LengthByString,
    filterAsName: filterAsName,

    loadPrefab: loadPrefab,
    loadResource: loadResource,

    copyToClipboard: copyToClipboard,
    
    Uint8ArrayToString: Uint8ArrayToString,

    attachCaptcha : attachCaptcha,
    convertArrayBufferToBase64: convertArrayBufferToBase64,

    requestDeviceOrientation: requestDeviceOrientation,
}
